/*
    This source file is part of GeNa project.
    Copyright © GeNa project 2008
    For the latest info, see http://code.google.com/p/genaproject/ or  http://lumpyproject.forums-rpg.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef EFFECT_H_INCLUDED
#define EFFECT_H_INCLUDED

#include <map>
#include <list>
#include <vector>
#include <fstream>
#include <string>

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
using namespace boost;

#include <GL/glew.h>
#include <GL/gl3.h>

#include <dom/domEffect.h>
#include <dom/domProfile_GLSL.h>

#include "math/matrix.h"
#include "material.h"

template<class T, unsigned int N> inline void glUniformv(GLint location, GLsizei count, T* values)
{
}

template<> inline void glUniformv<int,1>(GLint location, GLsizei count, int* values)
{
    glUniform1iv(location, count, values);
}

template<> inline void glUniformv<int,2>(GLint location, GLsizei count, int* values)
{
    glUniform2iv(location, count, values);
}

template<> inline void glUniformv<int,3>(GLint location, GLsizei count, int* values)
{
    glUniform3iv(location, count, values);
}

template<> inline void glUniformv<int,4>(GLint location, GLsizei count, int* values)
{
    glUniform4iv(location, count, values);
}

template<> inline void glUniformv<float,1>(GLint location, GLsizei count, float* values)
{
    glUniform1fv(location, count, values);
}

template<> inline void glUniformv<float,2>(GLint location, GLsizei count, float* values)
{
    glUniform2fv(location, count, values);
}

template<> inline void glUniformv<float,3>(GLint location, GLsizei count, float* values)
{
    glUniform3fv(location, count, values);
}

template<> inline void glUniformv<float,4>(GLint location, GLsizei count, float* values)
{
    glUniform4fv(location, count, values);
}

template<unsigned int N> inline void glUniformMatrixfv(GLint location, GLsizei count, GLboolean transpose, const float* values)
{
}

template<> inline void glUniformMatrixfv<2>(GLint location, GLsizei count, GLboolean transpose, const float* values)
{
    glUniformMatrix2fv(location, count, transpose, values);
}

template<> inline void glUniformMatrixfv<3>(GLint location, GLsizei count, GLboolean transpose, const float* values)
{
    glUniformMatrix3fv(location, count, transpose, values);
}

template<> inline void glUniformMatrixfv<4>(GLint location, GLsizei count, GLboolean transpose, const float* values)
{
    glUniformMatrix3fv(location, count, transpose, values);
}

class Effect;
class Technique;
class Pass;
class ParamBase;

class Effect
{
private:
    std::string name;

    std::vector<Technique*> techniques;

    std::map<std::string,ParamBase*> params;

    friend class Pass;
    friend class Material;
    friend class Model;
    friend class SamplerParam;
public:

    Effect(domEffect* effect)
    {
        loadEffect(effect);
    }

    void loadEffect(domEffect* effect);
};

class Technique
{
private:
    Effect* m_parent;

    std::vector<Pass*> passes;

    std::map<std::string,ParamBase*> params;

    friend class Pass;
    friend class Material;
    friend class Model;
    friend class SamplerParam;
public:
    Technique(Effect* parent, domProfile_GLSL::domTechniqueRef technique);
};

class Pass
{
private:
    Technique* m_parent;

    friend class SamplerParam;

    struct ClearTarget
    {
        GLenum clear; //A combination of GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT
        float clearColor[4];
        float clearDepth;
        int clearStencil;

        ClearTarget()
        {
            clear = 0;
        }
    };

    ClearTarget** clearTargets;
    unsigned int clearTargetsMax;

    #ifdef ARB_ASSEMBLY
    GLuint vertexProgramId, fragmentProgramId;
    #else
    GLuint programId;
    #endif

    //If ARB_ASSEMBLY is set, if the 31-bit is set, the Param is associated with a fragment program local variable, otherwise, it is associated with a vertex shader local variable ; the last bits describe the index
    std::list< std::pair<GLuint, ParamBase*> > binds;

    bool rsAlphaFunc;
    GLenum alphaFunc; //One of GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL.
    float alphaFuncValue;


    //One of GL_ZERO, GL_ONE, GL_SRC_COLOR_GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_SRC_ALPHA_SATURATE.
    bool rsBlendFunc;
    GLenum blendFuncSrc, blendFuncDest;

    bool rsBlendFuncSeparate;
    GLenum blendFuncSrcRGB, blendFuncDestRGB, blendFuncSrcAlpha, blendFuncDestAlpha;

    bool rsBlendEqu, rsBlendEquSeparate;
    GLenum blendEqu, blendEquRGB, blendEquAlpha; //One of GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX.

    bool rsCullMode;
    GLenum cullMode; //One of GL_FRONT, GL_BACK, GL_FRONT_AND_BACK

    bool rsDepthFunc;
    GLenum depthFunc; //One of GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL

    bool rsFrontFace;
    GLenum frontFace; //Either GL_CCW or GL_CW.

    bool rsLogicOp;
    GLenum logicOp; //One of GL_CLEAR, GL_COPY, GL_NOOP, GL_SET_GL_COPY_INVERTED, GL_INVERT, GL_AND_REVERSE, GL_OR_REVERSE, GL_AND, GL_OR, GL_NAND, GL_NOR, GL_XOR, GL_EQUIV, GL_AND_INVERTED, GL_OR_INVERTED.

    bool rsPolygonModeFront, rsPolygonModeBack, rsPolygonModeFrontAndBack;
    GLenum polygonModeFront, polygonModeBack, polygonModeFrontAndBack; //One of GL_NONE, GL_POINT, GL_LINE, GL_FILL.

    bool rsStencilFunc;
    GLenum stencilFunc; //One of GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL.
    unsigned int stencilRef;
    unsigned int stencilMask;

    bool rsStencilOp;
    //One of GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_INCR_WRAP, GL_DECR, GL_DECR_WRAP, GL_INVERT.
    GLenum stencilOpFail;
    GLenum stencilOpZFail;
    GLenum stencilOpZPass;

    bool rsStencilOpFront;
    GLenum stencilOpFailFront;
    GLenum stencilOpZFailFront;
    GLenum stencilOpZPassFront;

    bool rsStencilOpBack;
    GLenum stencilOpFailBack;
    GLenum stencilOpZFailBack;
    GLenum stencilOpZPassBack;

    bool rsStencilFuncSeparate;
    GLenum stencilFuncFront; //One of GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL.
    GLenum stencilFuncBack; //One of GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL.
    unsigned int stencilRefSeparate;
    unsigned int stencilMaskSeparate;

    bool rsStencilMaskFront;
    unsigned int stencilMaskFront;

    bool rsStencilMaskBack;
    unsigned int stencilMaskBack;

    std::vector<bool> clipPlanesEnable;
    std::vector<double*> clipPlanes;

    bool rsBlendColor;
    float blendColor[4];

    bool rsClearColor;
    float clearColor[4];

    bool rsClearStencil;
    int clearStencil;

    bool rsClearDepth;
    float clearDepth;

    bool rsColorMask;
    bool colorMask[4];

    bool rsDepthMask;
    bool depthMask;

    bool rsLineStipple;
    int lineStipple[2];

    bool rsLineWidth;
    float lineWidth;

    bool rsModelViewMatrix;
    math::matrix4f modelViewMatrix;

    bool rsPointSize;
    float pointSize;

    bool rsPolygonOffset;
    float polygonOffset[2];

    bool rsProjectionMatrix;
    math::matrix4f projectionMatrix;

    bool rsScissor;
    int scissor[4];

    bool rsStencilMask;
    bool rsAlphaTestEnable;
    bool rsBlendEnable;
    bool rsColorLogicOpEnable;
    bool rsCullFaceEnable;
    bool rsDepthTestEnable;
    bool rsLineSmoothEnable;
    bool rsLineStippleEnable;
    bool rsLogicOpEnable;
    bool rsMultisampleEnable;
    bool rsPointSmoothEnable;
    bool rsPolygonOffsetFillEnable;
    bool rsPolygonOffsetLinelEnable;
    bool rsPolygonOffsetPointlEnable;
    bool rsPolygonSmoothEnable;
    bool rsPolygonStippleEnable;
    bool rsSampleAlphaToCoverageEnable;
    bool rsSampleAlphaToOneEnable;
    bool rsSampleCoverageEnable;
    bool rsScissorTestEnable;
    bool rsStencilTestEnable;

    void init()
    {
        clearTargets = NULL;
        clearTargetsMax = 0;

        #ifdef ARB_ASSEMBLY
        vertexProgramId = 0;
        fragmentProgramId = 0;
        #else
        programId = 0;
        #endif

        rsAlphaFunc = false;
        rsBlendFunc = false;
        rsBlendFuncSeparate = false;
        rsBlendEqu = false;
        rsBlendEquSeparate = false;
        rsCullMode = false;
        rsDepthFunc = false;
        rsFrontFace = false;
        rsLogicOp = false;
        rsPolygonModeFront = false;
        rsPolygonModeBack = false;
        rsPolygonModeFrontAndBack = false;
        rsStencilFunc = false;
        rsStencilOp = false;
        rsStencilOpFront = false;
        rsStencilOpBack = false;
        rsStencilFuncSeparate = false;
        rsStencilMaskFront = false;
        rsStencilMaskBack = false;
        rsBlendColor = false;
        rsClearColor = false;
        rsClearStencil = false;
        rsClearDepth = false;
        rsColorMask = false;
        rsDepthMask = false;
        rsLineStipple = false;
        rsLineWidth = false;
        rsModelViewMatrix = false;
        rsPointSize = false;
        rsPolygonOffset = false;
        rsProjectionMatrix = false;
        rsScissor = false;
        rsStencilMask = false;
        rsAlphaTestEnable = false;
        rsBlendEnable = false;
        rsColorLogicOpEnable = false;
        rsCullFaceEnable = true;
        rsDepthTestEnable = true;
        rsLineSmoothEnable = false;
        rsLineStippleEnable = false;
        rsLogicOpEnable = false;
        rsMultisampleEnable = false;
        rsPointSmoothEnable = false;
        rsPolygonOffsetFillEnable = false;
        rsPolygonOffsetLinelEnable = false;
        rsPolygonOffsetPointlEnable = false;
        rsPolygonSmoothEnable = false;
        rsPolygonStippleEnable = false;
        rsSampleAlphaToCoverageEnable = false;
        rsSampleAlphaToOneEnable = false;
        rsSampleCoverageEnable = false;
        rsScissorTestEnable = false;
        rsStencilTestEnable = false;
    }

    void loadCode( std::string code, unsigned int id);

    void loadCode( domFx_code_profileRef code, unsigned int id)
    {
        loadCode( code->getValue(), id);
    }

    void loadInclude( domFx_include_commonRef code, unsigned int id)
    {
        std::string filename = code->getUrl().path();

        #ifdef WINDOWS
        if( filename[0] == '/')
            filename = filename.substr(1, filename.length()-1);
        #endif

        std::ifstream input( filename.c_str());

        if( !input)
            return;

        std::string str;

        char c = 1;
        while(  (c != '\0') && !input)
        {
            c = input.get();
            str += c;
        }

        loadCode( str, id);
    }

    template<class T> bool loadCodeAndInclude( T* from, unsigned int shaderId, std::string name)
    {
        for( unsigned int i = 0; i < from->getInclude_array().getCount(); i++)
        {
            if( name == from->getInclude_array()[i]->getSid())
            {
                loadInclude( from->getInclude_array()[i], shaderId);
                return true;
            }
        }

        for( unsigned int i = 0; i < from->getCode_array().getCount(); i++)
        {
            if( name == from->getCode_array()[i]->getSid())
            {
                loadCode( from->getCode_array()[i], shaderId);
                return true;
            }
        }

        return false;
    }

public:
    Pass(Technique* parent, domProfile_GLSL::domTechnique::domPassRef pass);

    void setupPass();
    void cleanPass()
    {

        #ifdef ARB_ASSEMBLY
        glDisable(GL_VERTEX_PROGRAM_ARB);
        glDisable(GL_FRAGMENT_PROGRAM_ARB);
        #else
        glUseProgram(0);
        #endif

        glPopAttrib();

        if( rsModelViewMatrix)
        {
            glMatrixMode( GL_MODELVIEW);
            glPopMatrix();
        }

        if( rsProjectionMatrix)
        {
            glMatrixMode( GL_PROJECTION);
            glPopMatrix();
        }

    }

    GLint getUniformLocation(std::string name)
    {
        #ifdef ARB_ASSEMBLY
        unsigned int id = 0;
        for( unsigned int i=0; i < name.length(); i++)
            id = id*10 + name[i] - '0';
        return id;
        #else
        return glGetUniformLocation( programId, name.c_str());
        #endif
    }


    GLint getAttribLocation(std::string name)
    {
        #ifdef ARB_ASSEMBLY
        unsigned int id = 0;
        for( unsigned int i=0; i < name.length(); i++)
            id = id*10 + name[i] - '0';
        return id;
        #else
        return glGetAttribLocation( programId, name.c_str());
        #endif
    }
};

class ParamBase
{
public:
    virtual void bind(unsigned int location) = 0;
    virtual void setParamValue(ParamBase* _param) = 0;
};

template<class T, unsigned int N> class Param : public ParamBase
{
private:
    T* values;
    unsigned int count;

    Param* param;
public:

    template<class U> Param( U val)
    {
        values = new T;
        *values = (T)val;
        count = 1;
        param = NULL;
    }

    Param(T* _values, unsigned int _count, bool copy = true)
    {
        count = _count;

        if( copy)
        {
            values = new T[count*N];

            for( unsigned int i=0; i < count*N; i++)
                values[i] = _values[i];
        }
        else
            values = _values;

        param = NULL;
    }

    void setParamValue(ParamBase* _param)
    {
        param = dynamic_cast<Param*>(_param);
    }

    template<class U> static Param* createFromDomArray(U array)
    {
        T* values = new T[N];

        for( unsigned int i=0; i< N; i++)
            values[i] = (T)(array->getValue()[i]);

        return new Param( values, 1, false);
    }

    void bind(unsigned int location)
    {
        #ifdef ARB_ASSEMBLY
        glGetProgramLocalParameterfvARB( (location & (1<<31)) ? GL_FRAGMENT_PROGRAM_ARB : GL_VERTEX_PROGRAM_ARB, location & ~(1<<31), (GLfloat*)values);
        #else
        glUniformv<T,N>(location, count, values);
        #endif
    }
};

template<unsigned int N> class MatrixParam : public ParamBase
{
private:
    float* values;
    unsigned int count;

    MatrixParam* param;
public:
    MatrixParam(float* _values, unsigned int _count, bool copy = true)
    {
        count = _count;

        if( copy)
        {
            values = new float[count*N*N];

            for( unsigned int i=0; i < count*N*N; i++)
                values[i] = _values[i];
        }
        else
            values = _values;
    }

    void setParamValue(ParamBase* _param)
    {
        param = dynamic_cast<MatrixParam*>(_param);
    }

    template<class U> static MatrixParam* createFromDomArray(U array)
    {
        float* values = new float[N];

        for( unsigned int i=0; i< N*N; i++)
            values[i] = (float)(array->getValue()[i]);

        return new MatrixParam( values, 1, false);
    }

    void bind(unsigned int location)
    {
        if( param)
        {
            param->bind(location);
            return;
        }

        #ifndef ARB_ASSEMBLY
        glUniformMatrixfv<N>(location, count, GL_TRUE, values);
        #endif
    }
};

class SurfaceParam : public ParamBase
{
private:
    GLenum type; //One of GL_NONE, GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_DEPTH, GL_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP.

    GLenum format; //One of OpenGl various internal texture formats

    unsigned int size[3];
    float viewportRatio[2];
    bool dynamic;

    unsigned int mipLevels;
    bool mipmapGenerate;

    SurfaceParam* param;

    GLuint id;

    bool loadSurfaceInitElements(domFx_surface_init_commonRef surface);
    GLenum getFace(domFx_surface_face_enum face);

    friend class SamplerParam;
public:
    template<class T> SurfaceParam(T surface);

    void setParamValue(ParamBase* _param)
    {
        param = dynamic_cast<SurfaceParam*>(_param);
    }

    void bind(unsigned int location)
    {
        if( param)
            return param->bind(location);
    }
};

#define TEXTURE_FORMAT(fm) if( str == # fm) format = GL_ ## fm ;
#define TEXTURE_ALIAS_FORMAT(fm, nfm) if( str == # fm) str = # nfm;

template<class T> SurfaceParam::SurfaceParam(T surface)
{
    param = NULL;

    //Determining type
    switch(surface->getType())
    {
        case FX_SURFACE_TYPE_ENUM_UNTYPED:
            type = GL_NONE;
            break;
        case FX_SURFACE_TYPE_ENUM_1D:
            type = GL_TEXTURE_1D;
            break;
	    case FX_SURFACE_TYPE_ENUM_2D:
            type = GL_TEXTURE_2D;
            break;
	    case FX_SURFACE_TYPE_ENUM_3D:
            type = GL_TEXTURE_3D;
            break;
	    case FX_SURFACE_TYPE_ENUM_RECT:
            type = GL_TEXTURE_RECTANGLE_ARB;
            break;
	    case FX_SURFACE_TYPE_ENUM_CUBE:
            type = GL_TEXTURE_CUBE_MAP;
            break;
	    case FX_SURFACE_TYPE_ENUM_DEPTH:
            type = GL_TEXTURE_DEPTH;
            break;
        default:
            break;
    }

    //Load some parameter
    if( surface->getSize())
    {
        domInt3 sz = surface->getSize()->getValue();
        size[0] = sz[0];
        size[1] = sz[1];
        size[2] = sz[2];
    }
    else
    {
        size[0] = 0; size[1] = 0; size[2] = 0;
    }

    if( surface->getViewport_ratio())
    {
        domFloat2 ratio = surface->getViewport_ratio()->getValue();
        viewportRatio[0] = ratio[0];
        viewportRatio[1] = ratio[1];
    }
    else
    {
        viewportRatio[0] = 0;
        viewportRatio[1] = 0;
    }

    if( surface->getMip_levels())
        mipLevels = surface->getMip_levels()->getValue();
    else
        mipLevels = 0;

    if( surface->getMipmap_generate())
        mipmapGenerate = surface->getMipmap_generate()->getValue();
    else
        mipmapGenerate = 0;

    //Determinig texture format.
    format = 0;

    if( surface->getFormat())
    {
        std::string str = surface->getFormat()->getValue();

        TEXTURE_ALIAS_FORMAT(R32G32B32A32_FLOAT, RGBA32F)
        TEXTURE_ALIAS_FORMAT(R32G32B32A32_UINT, RGBA32UI)
        TEXTURE_ALIAS_FORMAT(R32G32B32A32_SINT, RGBA32I)

        TEXTURE_ALIAS_FORMAT(R32G32B32_FLOAT, RGB32F)
        TEXTURE_ALIAS_FORMAT(R32G32B32_UINT, RGB32UI)
        TEXTURE_ALIAS_FORMAT(R32G32B32_SINT, RGB32I)

        TEXTURE_ALIAS_FORMAT(R16G16B16A16, RGB16)
        TEXTURE_ALIAS_FORMAT(R16G16B16A16_FLOAT, RGBA16F)
        TEXTURE_ALIAS_FORMAT(R16G16B16A16_UINT, RGBA16UI)
        TEXTURE_ALIAS_FORMAT(R16G16B16A16_UNORM, RGBA16)
        TEXTURE_ALIAS_FORMAT(R16G16B16A16_SINT, RGBA16I)
        TEXTURE_ALIAS_FORMAT(R16G16B16A16_SNORM, RGBA16_SNORM)

        TEXTURE_ALIAS_FORMAT(R32G32_FLOAT, RG32F)
        TEXTURE_ALIAS_FORMAT(R32G32_UINT, RG32UI)
        TEXTURE_ALIAS_FORMAT(R32G32_SINT, RG32I)

        TEXTURE_ALIAS_FORMAT(R10G10B10A2, RGB10_A2)
        TEXTURE_ALIAS_FORMAT(R11G11B10_FLOAT, R11F_G11F_B10F)

        TEXTURE_ALIAS_FORMAT(R8G8B8A8, RGBA8)
        TEXTURE_ALIAS_FORMAT(R8G8B8A8_UNORM, RGBA8)
        TEXTURE_ALIAS_FORMAT(R8G8B8A8_UNORM_SRGB, SRGB8_ALPHA8)
        TEXTURE_ALIAS_FORMAT(R8G8B8A8_UINT, RGBA8UI)
        TEXTURE_ALIAS_FORMAT(R8G8B8A8_SINT, RGBA8I)
        TEXTURE_ALIAS_FORMAT(R8G8B8A8_SNORM, RGBA8_SNORM)

        TEXTURE_ALIAS_FORMAT(R16G16, RG16)
        TEXTURE_ALIAS_FORMAT(R16G16_FLOAT, RG16F)
        TEXTURE_ALIAS_FORMAT(R16G16_UINT, RG16UI)
        TEXTURE_ALIAS_FORMAT(R16G16_UNORM, RG16)
        TEXTURE_ALIAS_FORMAT(R16G16_SINT, RG16I)
        TEXTURE_ALIAS_FORMAT(R16G16_SNORM, RG16_SNORM)

        TEXTURE_ALIAS_FORMAT(R32_FLOAT, R32F)
        TEXTURE_ALIAS_FORMAT(D32_FLOAT, DEPTH_COMPONENT32F)
        TEXTURE_ALIAS_FORMAT(R32_UINT, R32UI)
        TEXTURE_ALIAS_FORMAT(R32_SINT, R32I)

        TEXTURE_ALIAS_FORMAT(D24_UNORM_S8_UINT, DEPTH24_STENCIL8)

        TEXTURE_ALIAS_FORMAT(R8G8, RG8)
        TEXTURE_ALIAS_FORMAT(R8G8_UNORM, RG8)
        TEXTURE_ALIAS_FORMAT(R8G8_UINT, RG8UI)
        TEXTURE_ALIAS_FORMAT(R8G8_SINT, RG8I)
        TEXTURE_ALIAS_FORMAT(R8G8_SNORM, RG8_SNORM)

        TEXTURE_ALIAS_FORMAT(R16, RED16)
        TEXTURE_ALIAS_FORMAT(D16, DEPTH_COMPONENT16)
        TEXTURE_ALIAS_FORMAT(R16_FLOAT, R16F)
        TEXTURE_ALIAS_FORMAT(R16_UNORM, R16)
        TEXTURE_ALIAS_FORMAT(D16_UNORM, DEPTH_COMPONENT16)
        TEXTURE_ALIAS_FORMAT(R16_UINT, R16UI)
        TEXTURE_ALIAS_FORMAT(R16_SNORM, R16_SNORM)
        TEXTURE_ALIAS_FORMAT(R16_SINT, R16I)

        TEXTURE_ALIAS_FORMAT(R8, RED8)
        TEXTURE_ALIAS_FORMAT(R8_UNORM, R8)
        TEXTURE_ALIAS_FORMAT(R8_UINT, R8UI)
        TEXTURE_ALIAS_FORMAT(R8_SNORM, R8_SNORM)
        TEXTURE_ALIAS_FORMAT(R8_SINT, R8I)

        TEXTURE_ALIAS_FORMAT(A8, ALPHA8)
        TEXTURE_ALIAS_FORMAT(R9G9B9E5_SHAREDEXP, RGB9_E5)

        TEXTURE_FORMAT(ALPHA)
        TEXTURE_FORMAT(RED)
        TEXTURE_FORMAT(RG)
        TEXTURE_FORMAT(RGB)
        TEXTURE_FORMAT(RGBA)
        TEXTURE_FORMAT(RGB_SNORM)
        TEXTURE_FORMAT(RGBA_SNORM)

        TEXTURE_FORMAT(ALPHA4)
        TEXTURE_FORMAT(ALPHA8)
        TEXTURE_FORMAT(ALPHA12)
        TEXTURE_FORMAT(ALPHA16)
        TEXTURE_FORMAT(R8)
        TEXTURE_FORMAT(R8_SNORM)
        TEXTURE_FORMAT(R16)
        TEXTURE_FORMAT(R16_SNORM)
        TEXTURE_FORMAT(RG8)
        TEXTURE_FORMAT(RG8_SNORM)
        TEXTURE_FORMAT(RG16)
        TEXTURE_FORMAT(RG16_SNORM)
        TEXTURE_FORMAT(R3_G3_B2)
        TEXTURE_FORMAT(RGB4)
        TEXTURE_FORMAT(RGB5)
        TEXTURE_FORMAT(RGB8)
        TEXTURE_FORMAT(RGB8_SNORM)
        TEXTURE_FORMAT(RGB10)
        TEXTURE_FORMAT(RGB12)
        TEXTURE_FORMAT(RGB16)
        TEXTURE_FORMAT(RGB16_SNORM)
        TEXTURE_FORMAT(RGBA2)
        TEXTURE_FORMAT(RGBA4)
        TEXTURE_FORMAT(RGB5_A1)
        TEXTURE_FORMAT(RGBA8)
        TEXTURE_FORMAT(RGBA8_SNORM)
        TEXTURE_FORMAT(RGB10_A2)
        TEXTURE_FORMAT(RGBA12)
        TEXTURE_FORMAT(RGBA16)
        TEXTURE_FORMAT(RGBA16_SNORM)
        TEXTURE_FORMAT(SRGB)
        TEXTURE_FORMAT(SRGB8)
        TEXTURE_FORMAT(SRGB_ALPHA)
        TEXTURE_FORMAT(SRGB8_ALPHA8)
        TEXTURE_FORMAT(R16F)
        TEXTURE_FORMAT(RG16F)
        TEXTURE_FORMAT(RGB16F)
        TEXTURE_FORMAT(RGBA16F)
        TEXTURE_FORMAT(R32F)
        TEXTURE_FORMAT(RG32F)
        TEXTURE_FORMAT(RGB32F)
        TEXTURE_FORMAT(RGBA32F)
        TEXTURE_FORMAT(R11F_G11F_B10F)
        TEXTURE_FORMAT(RGB9_E5)
        TEXTURE_FORMAT(R8I)
        TEXTURE_FORMAT(R8UI)
        TEXTURE_FORMAT(R16I)
        TEXTURE_FORMAT(R16UI)
        TEXTURE_FORMAT(R32I)
        TEXTURE_FORMAT(R32UI)
        TEXTURE_FORMAT(RG8I)
        TEXTURE_FORMAT(RG8UI)
        TEXTURE_FORMAT(RG16I)
        TEXTURE_FORMAT(RG16UI)
        TEXTURE_FORMAT(RG32I)
        TEXTURE_FORMAT(RG32UI)
        TEXTURE_FORMAT(RGB8I)
        TEXTURE_FORMAT(RGB8UI)
        TEXTURE_FORMAT(RGB16I)
        TEXTURE_FORMAT(RGB16UI)
        TEXTURE_FORMAT(RGB32I)
        TEXTURE_FORMAT(RGB32UI)
        TEXTURE_FORMAT(RGBA8I)
        TEXTURE_FORMAT(RGBA8UI)
        TEXTURE_FORMAT(RGBA16I)
        TEXTURE_FORMAT(RGBA16UI)
        TEXTURE_FORMAT(RGBA32I)
        TEXTURE_FORMAT(RGBA32UI)

        TEXTURE_FORMAT(DEPTH_COMPONENT)
        TEXTURE_FORMAT(DEPTH_STENCIL)

        TEXTURE_FORMAT(DEPTH_COMPONENT16)
        TEXTURE_FORMAT(DEPTH_COMPONENT24)
        TEXTURE_FORMAT(DEPTH_COMPONENT32)
        TEXTURE_FORMAT(DEPTH_COMPONENT32F)
        TEXTURE_FORMAT(DEPTH24_STENCIL8)
        TEXTURE_FORMAT(DEPTH32F_STENCIL8)

        TEXTURE_FORMAT(COMPRESSED_RED)
        TEXTURE_FORMAT(COMPRESSED_RG)
        TEXTURE_FORMAT(COMPRESSED_RGB)
        TEXTURE_FORMAT(COMPRESSED_RGBA)
        TEXTURE_FORMAT(COMPRESSED_SRGB)
        TEXTURE_FORMAT(COMPRESSED_SRGB_ALPHA)
        TEXTURE_FORMAT(COMPRESSED_RED_RGTC1)
        TEXTURE_FORMAT(COMPRESSED_SIGNED_RED_RGTC1)
        TEXTURE_FORMAT(COMPRESSED_RG_RGTC2)
        TEXTURE_FORMAT(COMPRESSED_SIGNED_RG_RGTC2)
    }

    if( !format && surface->getFormat_hint())
    {
        domFx_surface_format_hint_commonRef formatHint = surface->getFormat_hint();

        bool srgb = false; bool compressable = false;

        domFx_surface_format_hint_common_complexType::domFx_surface_format_hint_common_complexType::domOption_Array & optionArray = formatHint->getOption_array();
        for( unsigned int i=0; i < optionArray.getCount(); i++)
        {
            if( optionArray[i]->getValue() == FX_SURFACE_FORMAT_HINT_OPTION_ENUM_SRGB_GAMMA)
                srgb = true;
            else if( optionArray[i]->getValue() == FX_SURFACE_FORMAT_HINT_OPTION_ENUM_COMPRESSABLE)
                compressable = true;
        }

        switch( formatHint->getChannels()->getValue())
        {
            case FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_RGB:
            case FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_XYZ:
                if( !formatHint->getPrecision())
                {
                    switch( formatHint->getRange()->getValue())
                    {
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                            format = GL_RGB_SNORM;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                            format = GL_RGB;
                            if( srgb)
                                format = GL_SRGB;
                            if( compressable)
                                format = GL_COMPRESSED_RGB;
                            if( srgb & compressable)
                                format = GL_COMPRESSED_SRGB;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                            format = GL_RGB32I;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                            format = GL_RGB32UI;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                            format = GL_RGB32F;
                            break;
                        default:
                            break;
                    }
                    break;
                }

                switch( formatHint->getPrecision()->getValue())
                {
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_LOW:
                        switch( formatHint->getRange()->getValue())
                        {
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                                format = GL_RGB8_SNORM;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                                format = GL_RGB8;
                                if( srgb)
                                    format = GL_SRGB8;
                                if( compressable)
                                    format = GL_COMPRESSED_RGB;
                                if( srgb & compressable)
                                    format = GL_COMPRESSED_SRGB;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                                format = GL_RGB8I;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                                format = GL_RGB8UI;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                                format = GL_RGB16F;
                                break;
                            default:
                                break;
                        }
                        break;
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_MID:
                        switch( formatHint->getRange()->getValue())
                        {
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                                format = GL_RGB16_SNORM;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                                format = GL_RGB16;
                                if( srgb)
                                    format = GL_SRGB;
                                if( compressable)
                                    format = GL_COMPRESSED_RGB;
                                if( srgb & compressable)
                                    format = GL_COMPRESSED_SRGB;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                                format = GL_RGB16I;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                                format = GL_RGB16UI;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                                format = GL_RGB16F;
                                break;
                            default:
                                break;
                        }
                        break;
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_HIGH:
                        switch( formatHint->getRange()->getValue())
                        {
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                                format = GL_RGB16_SNORM;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                                format = GL_RGB16;
                                if( srgb)
                                    format = GL_SRGB;
                                if( compressable)
                                    format = GL_COMPRESSED_RGB;
                                if( srgb & compressable)
                                    format = GL_COMPRESSED_SRGB;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                                format = GL_RGB32I;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                                format = GL_RGB32UI;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                                format = GL_RGB32F;
                                break;
                            default:
                                break;
                        }
                        break;

                        default:
                            break;
                }
                break;


            case FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_D:
                if( !formatHint->getPrecision())
                {
                    if( formatHint->getRange()->getValue() == FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT)
                        format = GL_DEPTH_COMPONENT32F;
                    else
                        format = GL_DEPTH_COMPONENT32;
                    break;
                }

                switch( formatHint->getPrecision()->getValue())
                {
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_LOW:
                        format = GL_DEPTH_COMPONENT16;
                        break;
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_MID:
                        format = GL_DEPTH_COMPONENT24;
                        break;
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_HIGH:
                        if( formatHint->getRange()->getValue() == FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT)
                            format = GL_DEPTH_COMPONENT32F;
                        else
                            format = GL_DEPTH_COMPONENT32;
                        break;
                    default:
                        break;
                }
                break;

            case FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_RGBA:
            case FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_XYZW:
            default:
                if( !formatHint->getPrecision())
                {
                    switch( formatHint->getRange()->getValue())
                    {
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                            format = GL_RGBA_SNORM;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                            format = GL_RGBA;
                            if( srgb)
                                format = GL_SRGB_ALPHA;
                            if( compressable)
                                format = GL_COMPRESSED_RGBA;
                            if( srgb & compressable)
                                format = GL_COMPRESSED_SRGB_ALPHA;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                            format = GL_RGBA32I;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                            format = GL_RGBA32UI;
                            break;
                        case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                            format = GL_RGBA32F;
                            break;
                        default:
                            break;

                    }
                    break;
                }

                switch( formatHint->getPrecision()->getValue())
                {
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_LOW:
                        switch( formatHint->getRange()->getValue())
                        {
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                                format = GL_RGBA8_SNORM;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                                format = GL_RGBA8;
                                if( srgb)
                                    format = GL_SRGB8_ALPHA8;
                                if( compressable)
                                    format = GL_COMPRESSED_RGBA;
                                if( srgb & compressable)
                                    format = GL_COMPRESSED_SRGB_ALPHA;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                                format = GL_RGBA8I;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                                format = GL_RGBA8UI;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                                format = GL_RGBA16F;
                                break;
                            default:
                                break;
                        }
                        break;
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_MID:
                        switch( formatHint->getRange()->getValue())
                        {
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                                format = GL_RGBA16_SNORM;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                                format = GL_RGBA16;
                                if( srgb)
                                    format = GL_SRGB8_ALPHA8;
                                if( compressable)
                                    format = GL_COMPRESSED_RGBA;
                                if( srgb & compressable)
                                    format = GL_COMPRESSED_SRGB_ALPHA;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                                format = GL_RGBA16I;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                                format = GL_RGBA16UI;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                                format = GL_RGBA16F;
                                break;
                            default:
                                break;
                        }
                        break;
                    case FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_HIGH:
                        switch( formatHint->getRange()->getValue())
                        {
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM:
                                format = GL_RGBA16_SNORM;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM:
                                format = GL_RGBA16;
                                if( srgb)
                                    format = GL_SRGB8_ALPHA8;
                                if( compressable)
                                    format = GL_COMPRESSED_RGBA;
                                if( srgb & compressable)
                                    format = GL_COMPRESSED_SRGB_ALPHA;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT:
                                format = GL_RGBA32I;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT:
                                format = GL_RGBA32UI;
                                break;
                            case FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT:
                                format = GL_RGBA32F;
                                break;
                            default:
                                break;
                        }
                        break;

                    default:
                        break;
                }
                break;
        }
    }

    if( !format)
        format = GL_RGBA8;

    dynamic = false;
    id = 0;

    if(!loadSurfaceInitElements(surface->getFx_surface_init_common()) && id)
    {
        glDeleteTextures(1, &id);
        id = 0;
    }
}

#include <boost/static_assert.hpp>

class SamplerParam : public ParamBase
{
private:
    //One of  GL_REPEAT, GL_MIRRORED_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER.
    GLenum wrap_s;
    GLenum wrap_t;
    GLenum wrap_r;

    //One of GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LIENAR.
    GLenum minFilter;
    GLenum magFilter;

    float borderColor[4];

    unsigned int mipmapMaxLevel;
    float mipmapBias;

    SurfaceParam* surface;


    SamplerParam* param;

    GLenum getWrap(domFx_sampler_wrap_common wrap);
    GLenum getFilter(domFx_sampler_filter_common filter);

    template<class T> GLenum getWrapT(T* sampler, typename disable_if_c< is_base_of<domFx_sampler1D_common_complexType, T>::value>::type* dummy = NULL)
    {
        if( sampler->getWrap_t())
            return getWrap( sampler->getWrap_t()->getValue());
        else
            return GL_REPEAT;
    }

    template<class T> GLenum getWrapT(T* sampler, typename enable_if_c< is_base_of<domFx_sampler1D_common_complexType, T>::value>::type* dummy = NULL)
    {
        return GL_REPEAT;
    }

    template<class T> GLenum getWrapR(T* sampler, typename enable_if_c< is_base_of<domFx_sampler3D_common_complexType, T>::value>::type* dummy = NULL)
    {
        if( sampler->getWrap_p())
            return getWrap( sampler->getWrap_p()->getValue());
        else
            return GL_REPEAT;
    }

    template<class T> GLenum getWrapR(T* sampler, typename disable_if_c< is_base_of<domFx_sampler3D_common_complexType, T>::value>::type* dummy = NULL)
    {
        return GL_REPEAT;
    }


    template<class T> void setUnusedParameterInDepthSamplerFrom(T* sampler, typename enable_if_c< is_base_of<domFx_samplerDEPTH_common_complexType, T>::value>::type* dummy = NULL)
    {
        borderColor[0] = 0; borderColor[1] = 0; borderColor[2] = 0; borderColor[3] = 0;
        mipmapMaxLevel = 0;
        mipmapBias = 0;
    }

    template<class T> void setUnusedParameterInDepthSamplerFrom(T* sampler, typename disable_if_c< is_base_of<domFx_samplerDEPTH_common_complexType, T>::value>::type* dummy = NULL)
    {
        if( sampler->getBorder_color())
        {
            domFx_color_common & c = sampler->getBorder_color()->getValue();
            borderColor[0] = c[0]; borderColor[1] = c[1]; borderColor[2] = c[2]; borderColor[3] = c[3];
        }
        else
        {
            borderColor[0] = 0; borderColor[1] = 0; borderColor[2] = 0; borderColor[3] = 0;
        }

        if( sampler->getMipmap_maxlevel())
            mipmapMaxLevel = sampler->getMipmap_maxlevel()->getValue();
        else
            mipmapMaxLevel = 0;
        if( sampler->getMipmap_bias())
            mipmapBias = sampler->getMipmap_bias()->getValue();
        else
            mipmapBias = 0;
    }
public:
    static unsigned int textureUnit;

    SurfaceParam* findSurface(std::string name, Material* material)
    {
        if( material->paramsByName[name])
            return dynamic_cast<SurfaceParam*>(material->paramsByName[name]);
        else
            return findSurface( name, material->technique);
    }

    SurfaceParam* findSurface(std::string name, Effect* effect)
    {
        return dynamic_cast<SurfaceParam*>(effect->params[name]);
    }

    SurfaceParam* findSurface(std::string name, Technique* technique)
    {
        if( technique->params[name])
            return dynamic_cast<SurfaceParam*>(technique->params[name]);
        return findSurface( name, technique->m_parent);
    }

    SurfaceParam* findSurface(std::string name, Pass* pass)
    {
        return findSurface( name, pass->m_parent);
    }

    template<class T, class U> SamplerParam(daeSmartRef<T> sampler, U surfaceSearchPoint )
    {
        param = NULL;
        if( sampler->getWrap_s())
            wrap_s = getWrap( sampler->getWrap_s()->getValue());
        else
            wrap_s = GL_REPEAT;

        wrap_t = getWrapT(sampler.cast());
        wrap_r = getWrapR(sampler.cast());

        if( sampler->getMinfilter())
            minFilter = getFilter(sampler->getMinfilter()->getValue());
        else
            minFilter = GL_LINEAR;

        if( sampler->getMagfilter())
            magFilter = getFilter(sampler->getMagfilter()->getValue());
        else
            minFilter = GL_LINEAR;

        surface = findSurface(std::string(sampler->getSource()->getValue()), surfaceSearchPoint);

        setUnusedParameterInDepthSamplerFrom(sampler.cast());
    }

    void setParamValue(ParamBase* _param)
    {
        param = dynamic_cast<SamplerParam*>(_param);
    }

    void bind(unsigned int location)
    {
        if( param)
            return param->bind(location);

        SurfaceParam* sf = surface;

        while( sf->param)
            sf = sf->param;

        if( sf->type == GL_NONE)
            return;

        GLenum target = (sf->type == GL_TEXTURE_DEPTH) ? GL_TEXTURE_2D : sf->type;
        switch( sf->type)
        {
            case GL_TEXTURE_3D:
            case GL_TEXTURE_CUBE_MAP:
                glTexParameteri(target,  GL_TEXTURE_WRAP_R,  wrap_r);
                break;
            case GL_TEXTURE_2D:
            case GL_TEXTURE_RECTANGLE_ARB:
            case GL_TEXTURE_DEPTH:
                glTexParameteri(target,  GL_TEXTURE_WRAP_T,  wrap_t);
                break;
            case GL_TEXTURE_1D:
                glTexParameteri(target,  GL_TEXTURE_WRAP_S,  wrap_s);
                break;
        }

        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        //glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
        //glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
        //glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR,  borderColor);
        //glTexParameteri( target, GL_TEXTURE_MAX_LEVEL, mipmapMaxLevel);
        //glTexParameterf( target, GL_TEXTURE_LOD_BIAS, mipmapBias);

        glEnable(GL_TEXTURE_2D);
        glActiveTexture( GL_TEXTURE0 + textureUnit);
        glBindTexture(target, sf->id);

        glUniform1i(location, textureUnit);
        textureUnit++;
    }
};

template<class T, class U> ParamBase* loadParamFromData(T data, U surfaceSearchPoint)
{
    //Unsupported Collada CORE_PARAM_TYPE : float{1x2, 1x3, 1x4, 2x1, 2x3, 2x4, 3x1, 3x2, 3x4, 4x1, 4x2, 4x3}

    if( data->getBool())
        return new Param<int, 1>( data->getBool()->getValue());
    else if( data->getBool2())
        return Param<int, 2>::createFromDomArray( data->getBool2());
    else if( data->getBool3())
        return Param<int, 3>::createFromDomArray( data->getBool3());
    else if( data->getBool4())
        return Param<int, 4>::createFromDomArray( data->getBool4());
    else if( data->getInt())
        return new Param<int, 1>( data->getInt()->getValue());
    else if( data->getInt2())
        return Param<int, 2>::createFromDomArray( data->getInt2());
    else if( data->getInt3())
        return Param<int, 3>::createFromDomArray( data->getInt3());
    else if( data->getInt4())
        return Param<int, 4>::createFromDomArray( data->getInt4());
    else if( data->getFloat())
        return new Param<float, 1>( data->getFloat()->getValue());
    else if( data->getFloat2())
        return Param<float, 2>::createFromDomArray( data->getFloat2());
    else if( data->getFloat3())
        return Param<float, 3>::createFromDomArray( data->getFloat3());
    else if( data->getFloat4())
        return Param<float, 4>::createFromDomArray( data->getFloat4());
    else if( data->getFloat2x2())
        return MatrixParam<2>::createFromDomArray( data->getFloat2x2());
    else if( data->getFloat3x3())
        return MatrixParam<3>::createFromDomArray( data->getFloat3x3());
    else if( data->getFloat4x4())
        return MatrixParam<4>::createFromDomArray( data->getFloat4x4());
    else if( data->getSurface())
        return new SurfaceParam( data->getSurface());
    else if( data->getSampler1D())
        return new SamplerParam( data->getSampler1D(),surfaceSearchPoint);
    else if( data->getSampler2D())
        return new SamplerParam( data->getSampler2D(),surfaceSearchPoint);
    else if( data->getSampler3D())
        return new SamplerParam( data->getSampler3D(),surfaceSearchPoint);
    else if( data->getSamplerCUBE())
        return new SamplerParam( data->getSamplerCUBE(),surfaceSearchPoint);
    else if( data->getSamplerRECT())
        return new SamplerParam( data->getSamplerRECT(),surfaceSearchPoint);
    else
        return new SamplerParam( data->getSamplerDEPTH(),surfaceSearchPoint);
}

#endif // EFFECT_H_INCLUDED
